﻿using System;
using System.Collections.Generic;
using System.Xml;
using IURIP;
using URIP.Tool;
using URIP.Factory;

namespace URIP.Core
{
    public class Task : ITask
    {
        protected Dictionary<string, ITarget> m_targets = new Dictionary<string, ITarget>();
        protected IAbstractor m_abstractor = null;
        protected IPropagator m_propagator = null;
        protected List<ITargetRelationship> m_targetRelationships = new List<ITargetRelationship>();
        protected Queue<IProcess> m_processes = new Queue<IProcess>();
        protected IRunable m_currentRunableObject = null;
        protected string m_id = "";
        protected string m_name = "";
        protected ILog m_log = new Logger();
        protected string m_connectionString = "";
        protected bool m_stopped = false;
        protected bool m_paused = false;

        public Dictionary<string, ITarget> Targets
        {
            get { return m_targets; }
        }
        public IAbstractor Abstractor
        {
            get { return m_abstractor; }
        }
        public IPropagator Propagator
        {
            get { return m_propagator; }
        }
        public List<ITargetRelationship> TargetRelationships
        {
            get { return m_targetRelationships; }
        }
        public Queue<IProcess> Processes
        {
            get { return m_processes; }
        }
        public IRunable CurrentRunableObject
        {
            get { return m_currentRunableObject; }
        }
        public string ID
        {
            get { return m_id; }
        }
        public string Name
        {
            get { return m_name; }
        }
        public ILog Logger
        {
            get { return m_log; }
        }
        public string ConnectionString
        {
            get { return m_connectionString; }
        }
        public bool Stopped
        {
            get { return m_stopped; }
        }
        public bool Paused
        {
            get { return m_paused; }
        }
        public bool SupportCache
        {
            get;
            set;
        }

        public bool InitByConfiguration(XmlDocument configurationFile, string name, string id, string connectionString)
        {
            bool res = false;
            m_connectionString = connectionString;
            Logger.LogInfo("Try to load configuration files.");
            try
            {
                if (string.IsNullOrEmpty(id))
                {
                    throw new Exception("Current task instance has no Guid");
                }
                m_id = id;
                m_name = name;
                this.InitAbstractor(configurationFile);
                this.InitPropagator(configurationFile);
                this.InitTargets(configurationFile);
                this.InitTargetRelationships(configurationFile);
                Propagator.Validate();
                res = true;
            }
            catch (Exception ex)
            {
                Logger.LogWarning("There are errors during loading configuration in this task, all data from source system have not been transfered to destination system!");
                Logger.LogError(ex);
                throw ex;
            }
            Logger.LogInfo("Finish loading all configuration files.");
            return res;

        }
        public void Run()
        {
            try
            {
                Logger.LogInfo("Try to strat this task {0}.", ID);
                //@@ Creates the start processes for Targets with IsDependent == false
                this.CreateStartProcesses();
                //@@ Run all processes
                while (Processes.Count > 0)
                {
                    if (Stopped)
                    {
                        Logger.LogInfo("The task is stopped menually!");
                        return;
                    }
                    Processes.Dequeue().Run(null);
                }
                Logger.LogInfo("Task runs successfully.");
            }
            catch (Exception ex)
            {
                Logger.LogError(string.Format("There is an error occured during executing task {0}.", ID));
                Logger.LogError(ex);
                throw ex;
            }
            finally
            {
            }
        }
        public void Stop()
        {
            m_stopped = true;
        }
        public void Pause()
        {
            m_paused = true;
        }
        public void Continue()
        {
            m_paused = false;
        }
        public void EnterRunableObject(IRunable runableObject)
        {
            m_currentRunableObject = runableObject;
            if (EnterRunableObjectHandler != null)
            {
                EnterRunableObjectHandler(runableObject);
            }
        }
        public void LeaveRunableObject(IRunable runableObject)
        {
            if (LeaveRunableObjectHandler != null)
            {
                LeaveRunableObjectHandler(runableObject);
            }
        }

        public event Action<IRunable> EnterRunableObjectHandler;
        public event Action<IRunable> LeaveRunableObjectHandler;

        private void SetLog4Net(string guid)
        {
            if (!string.IsNullOrEmpty(guid))
            {
                log4net.Appender.RollingFileAppender appender = new log4net.Appender.RollingFileAppender();
                appender.File = string.Format("c:\\{0}.log", guid);
                appender.AppendToFile = true;
                appender.MaxSizeRollBackups = 1;
                appender.MaximumFileSize = "100MB";
                appender.RollingStyle = log4net.Appender.RollingFileAppender.RollingMode.Size;
                appender.StaticLogFileName = true;
                appender.LockingModel = new log4net.Appender.FileAppender.MinimalLock();
                appender.Layout = new log4net.Layout.PatternLayout("%message%newline");//"%date [%thread] %-5level - %message%newline");
                appender.ActivateOptions();
                log4net.Repository.ILoggerRepository repository = log4net.LogManager.CreateRepository(guid);
                log4net.Config.BasicConfigurator.Configure(repository, appender);
                log4net.ILog innerLogger = log4net.LogManager.GetLogger(guid, guid + "-log");
                //todo ((Logger)m_log).RegisterLog4Net(innerLogger);
            }
        }
        private void CreateStartProcesses()
        {
            List<ITarget> noDependentTargets = this.GetNoDependentTargetList();
            if (noDependentTargets.Count == 0)
            {
                throw new Exception(string.Format("There is no process started. At least one target should be set: isDependent='false'"));
            }
            //@@ Queue all processes without dependency on other process
            for (int i = 0; i < noDependentTargets.Count; i++)
            {
                var process = noDependentTargets[i].ProcessTemplate.CreateRunable(null);
                Processes.Enqueue((IProcess)process);
            }
            Logger.LogInfo("Create {0} process(es) individual for starting this task.", noDependentTargets.Count);
        }
        private List<ITarget> GetNoDependentTargetList()
        {
            List<ITarget> res = new List<ITarget>();
            foreach (KeyValuePair<string, ITarget> kv in m_targets)
            {
                if (!kv.Value.IsDependent)
                {
                    res.Add(kv.Value);
                }
            }
            return res;
        }

        private void InitAbstractor(XmlDocument xml)
        {
            Logger.LogDebug(string.Format("Start loading Abstractor."));
            XmlNode abstractorNode = xml.SelectSingleNode("task/abstractor");
            if (abstractorNode == null)
            {
                throw new Exception(string.Format("There is no default Abstractor defined."));
            }
            m_abstractor = AbstractorFactory.GetAbstractor(abstractorNode, this);
            Logger.LogDebug("Finish loading Abstractor. There Abstractor name is '{0}'", m_abstractor.Name);
        }
        private void InitPropagator(XmlDocument xml)
        {
            Logger.LogDebug(string.Format("Start loading Propagator."));
            XmlNode propagatorNode = xml.SelectSingleNode("task/propagator");
            if (propagatorNode == null)
            {
                throw new Exception(string.Format("There is no Propagator defined."));
            }
            m_propagator = PropagatorFactory.GetPropagator(propagatorNode, this);
            Logger.LogDebug(string.Format("Finish loading Propagator. There Propagator name is '{0}'", m_propagator.Name));
        }
        private void InitTargets(XmlDocument xml)
        {
            XmlNodeList targetList = xml.SelectNodes("task/targets/target");
            Logger.LogDebug(string.Format("Start loading targets.There will be {0} targets loaded.", targetList.Count));
            for (int i = 0; i < targetList.Count; i++)
            {
                ITarget tmp = new Target(this);
                tmp.InitByConfiguration(targetList[i]);
                if (!String.IsNullOrEmpty(tmp.Name))
                {
                    if (Targets.ContainsKey(tmp.Name))
                    {
                        throw new Exception(string.Format("The target name '{0}' is repeated, it should be renamed", tmp.Name));
                    }
                    else
                    {
                        Targets.Add(tmp.Name, tmp);
                    }
                }
            }
            Logger.LogDebug(string.Format("Finish loading targets.There were {0} targets loaded.", Targets.Count));
        }
        private void InitTargetRelationships(XmlDocument xml)
        {
            XmlNodeList targetRelationshipList = xml.SelectNodes("task/targetRelationships/targetRelationship");
            Logger.LogDebug("Start loading TargetRelationships for task '{0}'. Quantity of defined target relationships is {1}.", Name, targetRelationshipList.Count);
            for (int i = 0; i < targetRelationshipList.Count; i++)
            {
                ITargetRelationship targetRelationship = new TargetRelationship(this);
                targetRelationship.InitByConfiguration(targetRelationshipList[i]);
                m_targetRelationships.Add(targetRelationship);
            }
            Logger.LogDebug("Finish loading TargetRelationships for task '{0}'. Quantity of loaded target relationship is {1}.", Name, TargetRelationships.Count);
        }
    }
}
